﻿using System;
using System.IO;
using System.Threading;
using System.Xml;

namespace MockingBird.Simulator.Framework.Handlers
{

    using System.Diagnostics;
    using Common.Infrastructure.Logging;
    using Configuration;
    using Exceptions;
    using Interfaces;
    using MockingBird.Simulator.Framework.Persistence;

    public sealed class SoapMessageHandler : MessageHandlerBase
    {

        #region "implementation of base properties"

        /// <summary>
        /// Read-Only property that returns the current Soap Request Message
        /// </summary>
        public override XmlDocument Request { get; protected set; }

        /// <summary>
        /// Read-Only Property that returns the current Soap Response Message
        /// </summary>
        public override XmlDocument Response { get; protected set; }

        /// <summary>
        /// read-only property that returns the short file name of the current soap Response message
        /// </summary>
        public override string ResponseFileName { get; protected set; }

        #endregion

        #region "constructor(s)"


        /// <summary>
        /// Default constructor
        /// </summary>
        public SoapMessageHandler() : base() { }

        public SoapMessageHandler(ILogger logger, IHandlerConfigurationManager configManager, IResponseBuilder responseBuilder, ISoapFaultBuilder faultBuilder, IRequestMessageLogger requestMessageLogger, IResponseMessageLogger responseMessageLogger, ApplicationConfiguration appConfig)
            : base(logger, configManager, responseBuilder, faultBuilder, requestMessageLogger, responseMessageLogger, appConfig)
        {
            Debug.WriteLine(string.Format("SoapMessageHandler.ctor on thread ID: {0}", Thread.CurrentThread.ManagedThreadId));

            this.Request = new XmlDocument();
            this.Response = new XmlDocument();
        }

        #endregion

        #region IMessageHandler Members

        public override void HandleMessage(IExecutionContext context)
        {
            this.logger.Log("SoapMessageHandler:HandleMessage()");
            base.HandleMessage(context);
        }
        #endregion

        #region "primary action methods"



        /// <summary>
        /// This method sets up the soap Request message from the context request
        /// </summary>
        //protected override void ProcessRequestMessage()
        //{
        //}

        /// <summary>
        /// This method passes the request message on to the appropriate logger
        /// </summary>
        //protected override void LogRequestMessage()
        //{
        //    this.logger.Log("SoapMessageHandler:LogRequestMessage()");
        //    this.requestLogger.LogMessage(this.context, this.Request);
        //}

        //protected override void SetupResponseMessage()
        //{
        //    this.logger.Log("SoapMessageHandler:SetupSoapResponseMessage()");

        //    OperationType operation = FindOperation();

        //    //Next we check the ResponseStrategy
        //    //if it is none, then we exit
        //    switch (operation.ResponseStrategy)
        //    {
        //        case ResponseStrategyType.Default:
        //            HandleDefaultResponse(operation);
        //            break;
        //        case ResponseStrategyType.XPath:
        //            HandleRequestXPathBasedResponse(operation);
        //            break;
        //        case ResponseStrategyType.Xslt:
        //            HandleRequestXsltBasedResponse(operation);
        //            break;
        //        case ResponseStrategyType.None:
        //            HandleNoResponse();
        //            break;

        //        default:
        //            this.logger.LogInfo("SetupSoapResponseMessage has reached the default segment of the switch case. Using the HandleNoResponse() option");
        //            HandleNoResponse();
        //            break;
        //    }


        //}

        //protected override void LogResponseMessage()
        //{
        //    this.logger.Log("SoapMessageHandler:LogResponseMessage()");

        //    if (!this.IsCurrentOperationOneWay)
        //    {
        //        this.responseLogger.LogMessage(this.context, this.Response);
        //    }

        //}

        //private void WriteResponseMessageToContext()
        //{
        //    this.logger.Log("SoapMessageHandler:WriteResponseMessageToContext()");
        //    if (!this.IsCurrentOperationOneWay)
        //    {
        //        context.WriteResponse(this.Response);
        //    }
        //}

        #endregion



        protected override void ExecuteFileBasedResponse(ResponseType response)
        {
            this.logger.Log("SoapMessageHandler:ExecuteFileBasedResponse()");

            //Find the response Root for the response file
            this.responseRoot = this.appConfiguration.GetResponseRootForEndpoint(context.EndpointName);

            //Update the responseFileName to be a complete path
            this.ResponseFileName = Path.Combine(responseRoot, response.FileName);

            try
            {
                // may cause an intended exception (i.e. require a fault) - if simulating mustUnderstand etc
                Response = responseBuilder.BuildResponse(this.ResponseFileName, GetSoapHeaders());
            }
            catch (Exception e)
            {
                Response = faultBuilder.BuildSoapFault(e);
            }



        }


        #region "private methods"

        private OperationType FindOperation()
        {
            // find could return null
            OperationType op = configManager.Find(CurrentOperationName);

            //if no method could be found then throw an exception          
            if (null == op)
            {
                ThrowException(new ApplicationConfigurationException(String.Format("No Operation could be found in configuration corresponding to operation name : {0} .. If derived from the SoapAction ensure that the format of the URI is correct.", CurrentOperationName)));
            }

            return op;
        }


        /// <summary>
        /// Defer to process any SOAP headers, may cause expected exception
        /// </summary>
        /// <returns>response soap headers as xml doc or null</returns>
        private XmlDocument GetSoapHeaders()
        {
            // argument null is allowed
            return new SoapHeaderBuilder(context, logger, appConfiguration, this.Request).ResponseSoapHeaders(FindOperation().SoapHeaderConfiguration);
        }


        private void ActOnResponse(ResponseType response)
        {
            this.logger.Log("SoapMessageHandler:ActOnResponse()");
            //The action now depends on what kind of response behavior is required
            switch (response.ResponseBehaviour)
            {
                case ResponseBehaviourType.Standard:
                    ExecuteFileBasedResponse(response);
                    break;
                case ResponseBehaviourType.Error:
                    //check if its a SoapFault or a timeout
                    if (response.ErrorType.Equals(ErrorType.SoapFault))
                        ExecuteFileBasedResponse(response);
                    else
                        ExecuteTimeout();
                    break;
                case ResponseBehaviourType.Variable:
                    break;
                default:
                    break;
            }


            //return;
        }

        private string GetFullPathOfFileInResponsesRoot(string filename)
        {
            responseRoot = String.Format("{0}\\{1}\\responses", endpointsRoot, context.EndpointName);
            return Path.Combine(responseRoot, filename);
        }


        #endregion

        private static XmlDocument ExtractSoapBody(XmlDocument soapRequest)
        {
            XmlDocument result = new XmlDocument();

            // TODO: this needs to be refactored so that request message handling
            // is encapsulated. The issue here is that in an ordinary ASMX based
            // soap message, the whole envelope is available whereas in the
            // WCF implementation only the internals of the body node are present.
            // For proper future processing we should be using the Message class.
            XmlNodeList xn = soapRequest.SelectNodes("//*[local-name()='Body']");
            if (xn.Count > 0)
            {
                result.LoadXml(xn[0].OuterXml);
            }
            else
            {
                result.LoadXml(soapRequest.OuterXml);
            }

            return result;
        }

    }
}
